package com.miya.demo.support.netty.server;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.miya.demo.support.netty.bean.ChatMsg;
import com.miya.demo.support.netty.bean.DataContent;
import com.miya.demo.support.netty.config.UserConnectPool;
import com.miya.demo.support.netty.enums.MessageActionEnum;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.handler.codec.http.websocketx.TextWebSocketFrame;
import io.netty.util.AttributeKey;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;

import java.util.Objects;

/**
 * 服务监听处理器
 *
 * @author CaiXiaowei
 * @date 2023/1/16
 */
@Data
@Slf4j
@ChannelHandler.Sharable
public class ServerListenerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {

    /**
     * 当建立链接时将Channel放置在Group当中
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("有新的客户端链接：[{}]", ctx.channel().id().asLongText());
        // 添加到channelGroup 通道组
        UserConnectPool.getChannelGroup().add(ctx.channel());
    }

    /**
     * 读取数据
     */
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, TextWebSocketFrame msg) throws Exception {
        String content = msg.text();
        if (StrUtil.isEmpty(content)) {
            handleConnect(ctx);
        }
        DataContent dataContent = JSONUtil.toBean(content, DataContent.class);
        Integer action = dataContent.getAction();
        ChatMsg chatMsg = dataContent.getChatMsg();
        switch(MessageActionEnum.getByCode(action)) {
            case CHAT:
                handleChat(ctx, chatMsg);
                break;
            case SIGNED:
                handleSigned(ctx, chatMsg);
                break;
            case KEEPALIVE:
                handleKeepAlive(ctx, chatMsg);
                break;
            case PULL_FRIEND:
                handlePullFriend(ctx, chatMsg);
                break;
            default:
                break;
        }
    }

    private void handleConnect(ChannelHandlerContext ctx) {
        String senderId = ctx.channel().id().asLongText();
        UserConnectPool.getChannelMap().put(senderId, ctx.channel());
        AttributeKey<String> key = AttributeKey.valueOf("userId");
        ctx.channel().attr(key).setIfAbsent(senderId);
    }

    private void handleChat(ChannelHandlerContext ctx, ChatMsg chatMsg) {
        Channel receiverChannel = UserConnectPool.getChannel(chatMsg.getReceiverId());
        if(receiverChannel == null) {
            //用户不在线
            return;
        }
        Channel findChannel = UserConnectPool.getChannelGroup().find(ctx.channel().id());
        if(findChannel == null) {
            //离线
            return;
        }
        receiverChannel.writeAndFlush(new TextWebSocketFrame(JSONUtil.toJsonStr(chatMsg)));
    }

    private void handleSigned(ChannelHandlerContext ctx, ChatMsg chatMsg) {
        // do nothing
    }

    private void handleKeepAlive(ChannelHandlerContext ctx, ChatMsg chatMsg) {
        // do nothing
    }

    private void handlePullFriend(ChannelHandlerContext ctx, ChatMsg chatMsg) {
        // do nothing
    }


    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("用户下线了:{}", ctx.channel().id().asLongText());
        // 删除通道
        UserConnectPool.getChannelGroup().remove(ctx.channel());
        removeUserId(ctx);
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //打印异常
        log.info("异常：{}", cause.getMessage());
        // 删除通道
        UserConnectPool.getChannelGroup().remove(ctx.channel());
        removeUserId(ctx);
        ctx.close();
    }

    /**
     * 删除用户与channel的对应关系
     */
    private void removeUserId(ChannelHandlerContext ctx) {
        AttributeKey<String> key = AttributeKey.valueOf("userId");
        String userId = ctx.channel().attr(key).get();
        UserConnectPool.getChannelMap().remove(userId);
    }

}
